今天來練習一下,在網頁資料中常見的 CRUDCRUD 是 Create(新增), Read(讀取), Update(更新), Delete(刪除
四個字的縮寫,
也是在資料庫操作中,最常見的4種操作,若以SQL的語法來看,有時會稱為新增(INSERT), 查詢(UPDATE), 刪除(DELETE), 修改(SELECT)
先來練習網頁前端的操作
參考在Vue官網上的範例
https://vuejs.org/examples/#crud
仍然是以 Composition+SFC 的模式為主
App.vue
<script setup>
import { ref, reactive, computed, watch } from 'vue'
const names = reactive(['Emil, Hans', 'Mustermann, Max', 'Tisch, Roman'])
const selected = ref('')
const prefix = ref('')
const first = ref('')
const last = ref('')
const filteredNames = computed(() =>
names.filter((n) =>
n.toLowerCase().startsWith(prefix.value.toLowerCase())
)
)
watch(selected, (name) => {
;[last.value, first.value] = name.split(', ')
})
function create() {
if (hasValidInput()) {
const fullName = `${last.value}, ${first.value}`
if (!names.includes(fullName)) {
names.push(fullName)
first.value = last.value = ''
}
}
}
function update() {
if (hasValidInput() && selected.value) {
const i = names.indexOf(selected.value)
names[i] = selected.value = `${last.value}, ${first.value}`
}
}
function del() {
if (selected.value) {
const i = names.indexOf(selected.value)
names.splice(i, 1)
selected.value = first.value = last.value = ''
}
}
function hasValidInput() {
return first.value.trim() && last.value.trim()
}
</script>
<template>
<div><input v-model="prefix" placeholder="Filter prefix"></div>
<select size="5" v-model="selected">
<option v-for="name in filteredNames" :key="name">{{ name }}</option>
</select>
<label>Name: <input v-model="first"></label>
<label>Surname: <input v-model="last"></label>
<div class="buttons">
<button @click="create">Create</button>
<button @click="update">Update</button>
<button @click="del">Delete</button>
</div>
</template>
<style>
* {
font-size: inherit;
}
input {
display: block;
margin-bottom: 10px;
}
select {
float: left;
margin: 0 1em 1em 0;
width: 14em;
}
.buttons {
clear: both;
}
button + button {
margin-left: 5px;
}
</style>
執行的結果如圖
//----------------------------------------
我們先從<template>
的內容看起,
可以看到這次要綁定的項目比較多
<input v-model="prefix" placeholder="Filter prefix">
<select size="5" v-model="selected">
<option v-for="name in filteredNames" :key="name">{{ name }}</option>
</select>
<label>Name: <input v-model="first"></label>
<label>Surname: <input v-model="last"></label>
<div class="buttons">
<button @click="create">Create</button>
<button @click="update">Update</button>
<button @click="del">Delete</button>
</div>
v-model
類的有 prefix,selected,first,last
相對的宣告
const selected = ref('')
const prefix = ref('')
const first = ref('')
const last = ref('')
v-for
類的有 "name in filteredNames"
const names = reactive(['Emil, Hans', 'Mustermann, Max', 'Tisch, Roman'])
names
是先宣告有3個元素 的 reactive()
const filteredNames = computed(() =>
names.filter((n) =>
n.toLowerCase().startsWith(prefix.value.toLowerCase())
)
)
這裡使用了 computed()
是指過濾的條件為names元素的文字先轉小寫文字後的字首與輸入的文字轉小寫文字相同,
回傳給filteredNames
這裡也可以變成 n.toLowerCase().includes(prefix.value.toLowerCase())
使用 includes 來過濾names元素的文字有包含prefix的元素
接著 <option v-for="name in filteredNames" :key="name">{{ name }}</option>
透過 v-for
取出 filteredNames
的每一個元素 name
,
同時 呈現在 {{ name }}
,並綁定到 :key
屬性
v-bind
類的有 :key="name"
@click
類的有 create,update,del
@click="create"
function create() {
if (hasValidInput()) {
const fullName = `${last.value}, ${first.value}`
if (!names.includes(fullName)) {
names.push(fullName)
first.value = last.value = ''
}
}
}
先檢查輪入資料是否正確 hasValidInput()
,如果 names 名單中 沒有fullName的話
就將fullName加入到names, names.push(fullName)
最後將輸入欄位 first.value,last.value
清空
再來是@click="update"
function update() {
if (hasValidInput() && selected.value) {
const i = names.indexOf(selected.value)
names[i] = selected.value = `${last.value}, ${first.value}`
}
}
hasValidInput()
先檢查輸入資料,同時 selected.value
有資料
讀取 selected.value在 names 名單中的索引值 i
然後將 selected.value =
${last.value}, ${first.value}傳給
names[i]`
最後是 @click="del"
function del() {
if (selected.value) {
const i = names.indexOf(selected.value)
names.splice(i, 1)
selected.value = first.value = last.value = ''
}
}
如果 selected.value 有資料的話
取得 selected.value 在 names 陣列大得索引值 i
接著從names的索引值i 的位置,刪除一個元素 names.splice(i, 1)
最後將輸入欄位 first.value,last.value
及select
選單中的選擇值 清空
最後是 hasValidInput()
function hasValidInput() {
return first.value.trim() && last.value.trim()
}
就是在檢查 first 及 last輸入欄位 去除空白字元後是否仍有輸入值
再來有看到 一個 watch()
watch(selected, (name) => {
;[last.value, first.value] = name.split(', ')
})
watch的對象是 selected,當有變動時會執行將該變動的name當作參數輸入,
執行 ;[last.value, first.value] = name.split(', ')
name.split(', ')
是將name 以 ,
為分隔字元 分隔成陣列
其傳到 [last.value, first.value]
//------------------------
這裡發現陣列開始前出現;
這個分號是為了讓有出現直接用陣列方括號來操作時,會與前一個程式碼結合造成錯誤,
JS規定可以在陣列方括號前加上;以作為程式碼之間隔開之用
看到這邊,想到JS也有一些建議的程式編寫格式的準則
JavaScript Standard Style
https://standardjs.com/readme-zhtw
https://standardjs.com/rules-zhtw#semicolons
雖然不是官方的,但是也提供了很完整的編寫建議,
也可以加裝在常用的編輯器上
其中有提到因為在程式碼格式中,最大習慣的改變是 程式碼句尾可以不用加分號
這個不加分號的方式,在遇到以( 、 [ 或
`當作程式的開頭時,容易誤認為是與前一程式串接在一起
所以有了在 陣列方括號前加上;的建議
像是以下這種狀況
watch(selected, (name) => {
console.log(selected.value)
[last.value, first.value] = name.split(', ')
})
會被編輯成
watch(selected, (name) => {
console.log(selected.value)[last.value, first.value] = name.split(', ')
})
而產生錯誤
所以要改寫成
watch(selected, (name) => {
console.log(selected.value);
[last.value, first.value] = name.split(', ')
})
這樣雖然可以但是不建議
要寫成這樣才是建議的
watch(selected, (name) => {
console.log(selected.value)
;[last.value, first.value] = name.split(', ')
})
第一種會理解成是程式碼句尾加上;
第二種則是理解成做為一種提示作用,代表這裡有以方括號為程式開始的程式碼
//------------------
由以上的解析後,發現CRUD都是陣列元素的操作
同時 互相綁定,監看及 更新的關係很密切,在維護上要對彼此的連動關係很清楚,
才應確保資料可以正確操作